package com.vtron.web.api.advice;

import com.alibaba.fastjson.JSON;
import com.vtron.core.user.LoggedUser;
import com.vtron.web.api.annotation.OperLog;
import com.vtron.web.api.models.SysLogInfo;
import com.vtron.web.api.service.ISysLogInfoService;
import com.vtron.web.api.service.ITokenService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.UUID;

/**
 * 切面处理，操作日志
 *
 * @date : 2022/3/30 11:44
 * @author: 忻龙彪
 */
@Aspect
@Component
public class OperLogAspect {
    private final Logger logger = LoggerFactory.getLogger(OperLogAspect.class);

    @Autowired(required = false)
    private ISysLogInfoService sysLogInfoService;
    @Autowired(required = false)
    private ITokenService tokenService;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.vtron.web.api.annotation.OperLog)")
    public void operLogPoinCut() {
    }

/*   /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作

    @Pointcut("execution(* vtron.component.service.impl.*.*(..))")
    public void operExceptionLogPoinCut() {
    }
*/

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     * @param refObj    返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "refObj")
    public void afterOperLog(JoinPoint joinPoint, Object refObj) {
        LoggedUser loggedUser = getLoggedUser();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取注解内容
        OperLog operLog = method.getAnnotation(OperLog.class);
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = String.format("%s.%s", className, method.getName());
        String resultJson = Objects.isNull(refObj) ? "" : JSON.toJSONString(refObj);
        SysLogInfo log = new SysLogInfo();
        log.setTitle(operLog.title());
        log.setContent(resultJson);
        log.setMethodName(methodName);
        log.setOper(operLog.oper());
        log.setModel(operLog.model());
        log.setParamObjs(joinPoint.getArgs());
        log.setCreateIdBy(loggedUser.getId());
        log.setCreateNameBy(loggedUser.getName());
        //logger.info("afterOperLog.methodName=" + methodName);
        //logger.info("afterOperLog.result=" + resultJson);
        //logger.info("afterOperLog.log=" + log);

        save(log);
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operLogPoinCut()", throwing = "e")
    public void exceptionLog(JoinPoint joinPoint, Throwable e) {
        LoggedUser loggedUser = getLoggedUser();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取注解内容
        OperLog operLog = method.getAnnotation(OperLog.class);
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = String.format("%s.%s", className, method.getName());
        SysLogInfo log = new SysLogInfo();
        log.setTitle(operLog.title());
        log.setLevel("ERROR");
        log.setContent(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
        log.setMethodName(methodName);
        log.setOper(operLog.oper());
        log.setModel(operLog.model());
        log.setParamObjs(joinPoint.getArgs());
        log.setCreateIdBy(loggedUser.getId());
        log.setCreateNameBy(loggedUser.getName());
        //logger.info("exceptionLog.methodName=" + methodName);
        //logger.info("exceptionLog.error=" + log);
        save(log);
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    private String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }

    private String toUuId() {
        String uuid = UUID.randomUUID().toString();
        uuid = uuid.replace("-", "");
        return uuid;
    }

    private void save(SysLogInfo log) {
        if (sysLogInfoService != null) {
            try {
                sysLogInfoService.save(log);
            } catch (Exception ex) {
                ex.printStackTrace();
                logger.info("Failed to save log because sysloginfoservice is error!");
            }
        } else {
            logger.info("Failed to save log because sysloginfoservice is null!");
        }
    }

    private LoggedUser getLoggedUser() {
        LoggedUser user = new LoggedUser("0000", "未知");
        if (tokenService != null) {
            user = tokenService.getAuthentication(LoggedUser.class,
                    token -> new LoggedUser("0000", "未知"));
        }
        return user;
    }
}
